#ifndef FPAL_HPP
#define FPAL_HPP

#include "ap_fixed.h" // Xilinx 定点数类型
#include "hls_math.h" // HLS 数学函数
#include <iostream>   // 用于调试输出
#include <limits>     // 用于数值极限
#include <type_traits> // 用于 std::is_same

// -----------------------------------------------------------
// 封装在命名空间 fpal 中
// -----------------------------------------------------------
namespace fpal {

// --- 1. 定点数类型定义 ---

// fp_coord_t: 用于表示空间坐标，需要较大的范围和一定的精度。
// W=32, I=18 (S.17), F=14 (32-18=14)。范围 [-2^17, 2^17 - 2^-14), 即 [-131072.0, ~131072.0)。
// 这个范围足以容纳 1280x720 屏幕坐标以及常见的模型/世界/裁剪空间坐标。
// 使用AP_TRN (截断) 和 AP_SAT (饱和) 模式，以提供更好的数值健壮性。
typedef ap_fixed<32, 18, AP_TRN, AP_SAT> fp_coord_t;

// fp_vec_unit_t: 用于表示单位向量分量、UV 纹理坐标，或矩阵中的旋转/缩放分量。
// W=24, I=3 (S.2), F=21 (24-3=21)。范围 [-4.0, ~4.0)。
// 使用AP_TRN (截断) 和 AP_SAT (饱和) 模式。
typedef ap_fixed<24, 3, AP_TRN, AP_SAT> fp_vec_unit_t;

// fp_z_buffer_t: 用于表示深度缓冲区中的深度值。
// U.16.0，即 16 位无符号整数。范围 [0, 65535]。
typedef ap_uint<16> fp_z_buffer_t;

// fp_color_channel_t: 用于表示颜色通道，通常是 0-255 的无符号整数。
// U.8.0，即 8 位无符号整数。范围 [0, 255]。
typedef ap_uint<8> fp_color_channel_t;

// fp_color_scale_t: 专门用于颜色转换的中间类型，确保能容纳 255.0。
// W=21, I=9 (S.8), F=12。范围 [-256.0, ~256.0)。
// 增加小数位F=12，以提高精度，解决四舍五入问题。
typedef ap_fixed<21, 9, AP_TRN, AP_SAT> fp_color_scale_t;


// 向量结构体
struct vec3_fp_coord {
    fp_coord_t x;
    fp_coord_t y;
    fp_coord_t z;
};

struct vec3_fp_vec_unit {
    fp_vec_unit_t x;
    fp_vec_unit_t y;
    fp_vec_unit_t z;
};

// 钳制函数 (clamp) - 定义在头文件
template<typename T>
T clamp(T val, T min_val, T max_val) {
    #pragma HLS INLINE // 可以保留，因为这个函数非常简单，没有内部循环
    if (val < min_val) return min_val;
    if (val > max_val) return max_val;
    return val;
}

// 饱和函数 (saturate, 钳制到 [0, 1]) - 定义在头文件
template<typename T>
T saturate(T val) {
    #pragma HLS INLINE // 可以保留
    return clamp(val, (T)0.0, (T)1.0);
}


// -----------------------------------------------------------
// 2. 基础算术运算集合 (模板函数，支持不同 ap_fixed 类型)
// -----------------------------------------------------------

template<int W, int I, ap_q_mode Q, ap_o_mode O, int N>
ap_fixed<W, I, Q, O, N> add(ap_fixed<W, I, Q, O, N> a, ap_fixed<W, I, Q, O, N> b) {
    #pragma HLS INLINE
    return a + b;
}

template<int W, int I, ap_q_mode Q, ap_o_mode O, int N>
ap_fixed<W, I, Q, O, N> sub(ap_fixed<W, I, Q, O, N> a, ap_fixed<W, I, Q, O, N> b) {
    #pragma HLS INLINE
    return a - b;
}

template<int W, int I, ap_q_mode Q, ap_o_mode O, int N>
ap_fixed<W, I, Q, O, N> mul(ap_fixed<W, I, Q, O, N> a, ap_fixed<W, I, Q, O, N> b) {
    #pragma HLS INLINE
    return a * b; // HLS 将映射到 DSP Slice，通常有 3 级流水线
}

template<int W, int I, ap_q_mode Q, ap_o_mode O, int N>
ap_fixed<W, I, Q, O, N> div(ap_fixed<W, I, Q, O, N> a, ap_fixed<W, I, Q, O, N> b) {
    #pragma HLS INLINE
    ap_fixed<W, I, Q, O, N> zero_val = (ap_fixed<W, I, Q, O, N>)0;

    if (b == zero_val) {
        #ifndef __SYNTHESIS__
        std::cerr << "Warning: Division by zero! Returning 0." << std::endl;
        #endif
        return zero_val; // 除数为0，返回0
    }
    // 对于非零除数，执行除法。ap_fixed的AP_SAT模式会自动处理结果溢出。
    return a / b;
}

// --- 复杂运算集合 - 自定义迭代实现 sqrt/inv_sqrt ---

// 牛顿-拉夫逊迭代法计算倒数平方根 (1/sqrt(val))
// x_next = x * (1.5 - 0.5 * val * x^2)
template<typename T, int NUM_ITERATIONS>
T custom_inv_sqrt_iterative(T val) {
    T zero_val = (T)0;
    if (val <= zero_val) return zero_val;

    T x0;
    if (std::is_same<T, fp_coord_t>::value) {
        // 此处的优化策略需要根据实际精度需求和资源限制进行微调
        if (val >= (T)10000.0)      x0 = (T)0.003; 
        else if (val >= (T)100.0)   x0 = (T)0.05;
        else if (val >= (T)4.0)     x0 = (T)0.2;
        else if (val >= (T)1.0)     x0 = (T)0.7;
        else if (val >= (T)0.1)     x0 = (T)2.0; // 1/sqrt(0.1) ~ 3.16
        else if (val >= (T)0.01)    x0 = (T)5.0; // 1/sqrt(0.01) = 10
        else                        x0 = (T)10.0; // 对于更小的正数
    } else if (std::is_same<T, fp_vec_unit_t>::value) {
        // fp_vec_unit_t (I=3) 的初始猜测，现在可以容纳 3.33...
        if (val >= (T)1.0)          x0 = (T)0.8;
        else if (val >= (T)0.5)     x0 = (T)1.0;
        else if (val >= (T)0.1)     x0 = (T)1.5;
        // else if (val >= (T)0.01) x0 = (T)5.0; // 例如，如果需要处理更小的fp_vec_unit_t输入，可以添加
        else                        x0 = (T)2.0; // 对于更小的正数，如 0.09，期望 3.33，x0=2.0 仍是合理起点
    } else {
        x0 = (T)0.5; // 默认值
    }
    
    T half = (T)0.5;
    T three_half = (T)1.5;

    // 获取 ap_fixed 类型的实际位宽和整数位宽
    // 注意：T::width 和 T::iwidth 是 C++ 模板参数，可以直接访问
    const int T_WIDTH_ACTUAL = T::width;
    const int T_IWIDTH_ACTUAL = T::iwidth;

    for (int i = 0; i < NUM_ITERATIONS; ++i) { 
        #pragma HLS LOOP_TRIPCOUNT min=NUM_ITERATIONS max=NUM_ITERATIONS
        #pragma HLS PIPELINE II=1
        
        T val_times_x0_sq = mul(mul(val, x0), x0);
        
        T term = sub(three_half, mul(half, val_times_x0_sq));
        
        // 确保 term 保持正值，且不超过 three_half。这对于防止 x0 符号翻转至关重要。
        x0 = mul(x0, clamp(term, zero_val, three_half)); 
    }
    return x0;
}



// 牛顿-拉夫逊迭代法计算平方根 (sqrt(val))
// 通过 inv_sqrt 后乘以 val 实现: sqrt(x) = x * (1/sqrt(x))
template<typename T, int NUM_ITERATIONS>
T custom_sqrt_iterative(T val) {
    // 此模板函数不应加 #pragma HLS INLINE，因为它的非模板包装函数将会决定是否内联
    T zero_val = (T)0;
    if (val <= zero_val) return zero_val;

    T inv_s = custom_inv_sqrt_iterative<T, NUM_ITERATIONS>(val);
    return mul(val, inv_s);
}


// 非模板函数声明 (供外部使用，指定类型和迭代次数)
// 迭代次数设定为5次，以平衡精度和延迟。
// 这些函数将在 fpal.cpp 中定义，并且不再强制内联，以实现资源共享。
fp_coord_t sqrt(fp_coord_t val);
fp_vec_unit_t sqrt(fp_vec_unit_t val);
fp_coord_t inv_sqrt(fp_coord_t val);
fp_vec_unit_t inv_sqrt(fp_vec_unit_t val);


// 四舍五入到最近整数的辅助函数 (用于颜色转换) - 定义在头文件
// 注意：该函数期望输入值已经是 [0, 255] 范围内的定点数
template<typename T>
fp_color_channel_t round_to_nearest_uint8(T val) {
    #pragma HLS INLINE // 可以保留
    // 将值加上0.5，然后转换为无符号整数，ap_fixed的to_uint()会进行截断。
    // 由于之前已经 clamp 到 [0, 255]，这里不再需要额外的 clamp。
    T rounded_val = val + (T)0.5; 
    return (fp_color_channel_t)rounded_val.to_uint();
}


// -----------------------------------------------------------
// 3. 线性代数运算集合 - 前置声明
// -----------------------------------------------------------

// 向量加法
vec3_fp_coord vec3_add(vec3_fp_coord v1, vec3_fp_coord v2);
vec3_fp_vec_unit vec3_add(vec3_fp_vec_unit v1, vec3_fp_vec_unit v2);

// 向量减法
vec3_fp_coord vec3_sub(vec3_fp_coord v1, vec3_fp_coord v2);
vec3_fp_vec_unit vec3_sub(vec3_fp_vec_unit v1, vec3_fp_vec_unit v2);

// 点积
fp_vec_unit_t vec3_dot(vec3_fp_vec_unit v1, vec3_fp_vec_unit v2);
fp_coord_t vec3_dot(vec3_fp_coord v1, vec3_fp_coord v2);

// 叉积
vec3_fp_vec_unit vec3_cross(vec3_fp_vec_unit v1, vec3_fp_vec_unit v2);
vec3_fp_coord vec3_cross(vec3_fp_coord v1, vec3_fp_coord v2);

// 向量长度平方
fp_vec_unit_t vec3_length_sq(vec3_fp_vec_unit v);
fp_coord_t vec3_length_sq(vec3_fp_coord v);

// 向量长度
fp_vec_unit_t vec3_length(vec3_fp_vec_unit v);
fp_coord_t vec3_length(fp_coord_t x, fp_coord_t y, fp_coord_t z); // 额外重载，以支持直接传入分量
fp_coord_t vec3_length(vec3_fp_coord v); 

// 向量归一化
vec3_fp_vec_unit vec3_normalize(vec3_fp_vec_unit v);
vec3_fp_coord vec3_normalize(vec3_fp_coord v);

// 矩阵-向量乘法 (4x4 矩阵 * 4元素向量)
// const fp_coord_t mat[4][4] 也可以写成 const fp_coord_t mat[16] 以简化 HLS 接口
vec3_fp_coord mat4_vec4_mul(const fp_coord_t mat[4][4], vec3_fp_coord vec_xyz, fp_coord_t w_in);

// 矩阵-向量乘法 (3x3 矩阵 * 3元素向量)
// const fp_vec_unit_t mat[3][3] 也可以写成 const fp_vec_unit_t mat[9]
vec3_fp_vec_unit mat3_vec3_mul(const fp_vec_unit_t mat[3][3], vec3_fp_vec_unit vec);


// -----------------------------------------------------------
// 4. 定点数类型转换函数 - 前置声明
// -----------------------------------------------------------

fp_vec_unit_t conv_coord_to_vec_unit(fp_coord_t val);
fp_coord_t conv_vec_unit_to_coord(fp_vec_unit_t val);
fp_color_channel_t conv_fp_to_color_channel(fp_vec_unit_t val);
fp_color_channel_t conv_fp_to_color_channel(fp_coord_t val);

} // namespace fpal


// -----------------------------------------------------------
// 5. HLS 顶层函数声明 (全局命名空间，用于HLS工具识别)
// -----------------------------------------------------------
void fpal_wrapper(
    fpal::fp_coord_t in_coord_x,
    fpal::fp_coord_t in_coord_y,
    fpal::fp_coord_t in_coord_z,
    fpal::fp_vec_unit_t in_vec_u,
    fpal::fp_vec_unit_t in_vec_v,
    fpal::fp_color_channel_t &out_color_r,
    fpal::fp_coord_t &out_coord_len_sq,
    fpal::fp_vec_unit_t &out_vec_norm_x
);


#endif // FPAL_HPP
